<?php 
/**
 * Archivable Behavior class file.
 *
 * Model Behavior to support moving a deleted record to it's respective archive table when
 * that record is deleted.
 *
 * This behavior implements the beforeDelete() callback for moving the
 * specified record to the respective archive table.
 *
 * Usage in model:
 *
 * Add Archivable to the $actsAs array of your model:
 * var $actsAs = array('Archivable' => array('table' => 'model_archive_table_name'));
 *
 * @filesource
 * @author		Sohaib Muneer
 * @link		http://blog.sohaibmuneer.com/cakephp-archivable-behavior
 * @license		http://www.opensource.org/licenses/mit-license.php The MIT License
 * @package		app
 * @subpackage	app.models.behaviors
 */

/**
 * Add Archivable Behavior to a model
 *
 */
class ArchivableBehavior extends ModelBehavior {
	/**
	 * Contains settings indexed by Model name
	 *
	 * @var unknown_type
	 */
	public $settings = array();
/**
 * Instantiate settings for Model
 *
 * @param Object $Model
 * @param Array $settings
 */
	public function setup ( &$Model, $settings ) {
		if (!isset($this->settings[$Model->alias])) {
			$archivedTable = $Model->useTable.'_archived';
			$this->settings[$Model->alias] = array(
				'table' => $archivedTable
			);
		}
		
		$this->settings[$Model->alias] = array_merge(
			$this->settings[$Model->alias]
			, (array) $settings
		);
	}
/**
 * Get Clone of Model with settings changed to Archived Table
 *
 * @access Private
 * @param Object $Model
 * @return Object
 */
	private function &_getArchivedModel ( &$Model ) {
		$_Model = clone $Model;
		$_Model->useTable = $this->settings[$Model->alias]['table'];
		$_Model->table = $_Model->useTable;
		$_Model->alias = Inflector::camelize($_Model->useTable);
		return $_Model;
	}
/**
 * Encapsulates Model Archive Functionality
 *
 * @access Protected
 * @param Object $Model
 * @return Boolean	False on faliure - True on Success
 */
	protected function _archive ( &$Model ) {
		$Model->recursive = -1;
		$data = $Model->read();
		
		if (empty($data)) return false;
		
		$_Model = $this->_getArchivedModel($Model);
		
		$_data[$_Model->alias] = $data[$Model->alias];
		
		$fKeyField = Inflector::underscore($Model->alias).'_id';
		$_data[$_Model->alias][$fKeyField] = $data[$Model->alias]['id'];
		unset($_data[$_Model->alias]['id']);
		
		$dbo = $_Model->getDataSource();
		
		return $dbo->create(
			$_Model
			, array_keys($_data[$_Model->alias])
			, array_values($_data[$_Model->alias])
		);
	}
/**
 * Encapsulates UN-Archive Model functionality
 *
 * @access Protected
 * @param Object $Model
 * @return Mixed	On success Model::$data if its not empty or true, false on failure
 */
	protected function _unarchive ( &$Model ) {
		$_Model = $this->_getArchivedModel($Model);
		$fKeyField = Inflector::underscore($Model->alias).'_id';
		$_Model->recursive = -1;
		$_data = $_Model->find(
			'first'
			, array(
				'conditions' => array(
					$_Model->alias.'.'.$fKeyField => $Model->id
				)
			)
		);
		
		if (empty($_data)) return false;
		
		$data[$Model->alias] = $_data[$_Model->alias];
		$data[$Model->alias]['id'] = $Model->id;
		unset($data[$Model->alias][$fKeyField]);
		
		$_Model->Behaviors->detach('Archivable');
		$_Model->del($_data[$_Model->alias]['id'], false);
		
		$Model->create($data);
		return $Model->save($data);
	}
/**
 * Wrapper function for protected $this::_archive() method
 * And CakePHP callback, executed before every delete statement
 *
 * @access Public
 * @param Object $Model
 * @param Boolean $cascade
 * @return Boolean	False on faliure - True on Success
 */
	public function beforeDelete ( &$Model, $cascade = true ) {
		return $this->_archive($Model);
	}
/**
 * Enter description here...
 *
 * @param Object $Model
 * @param Mixed $id	Depends what kind of Id Model uses, can use both INT and CHAR (UUID) Ids
 * @return Mixed	On success Model::$data if its not empty or true, false on failure
 */
	public function unarchive ( &$Model, $id = null) {
		if (!$id) {
			if (!$Model->id) trigger_error('Model Id not specified', E_USER_ERROR);
			$id = $Model->id;
		}
		
		$Model->id = $id;
		return $this->_unarchive($Model);
	}
}
?>